{"id":1712,"date":"2023-01-22T23:11:25","date_gmt":"2023-01-23T07:11:25","guid":{"rendered":"https:\/\/surfrock66.com\/?p=1712"},"modified":"2023-02-25T08:48:44","modified_gmt":"2023-02-25T16:48:44","slug":"openldap-kerberos-and-sasl-my-experience-in-the-homelab","status":"publish","type":"post","link":"https:\/\/surfrock66.com\/?p=1712","title":{"rendered":"OpenLDAP, Kerberos and SASL &#8211; My Experience In The Homelab"},"content":{"rendered":"<p>I previously posted my experiences <a href=\"https:\/\/surfrock66.com\/my-experience-guide-setting-up-openldap-for-pc-webapp-authentication-on-ubuntu-20-04\/\" title=\"setting up OpenLDAP on Ubuntu Server\">setting up OpenLDAP on Ubuntu Server<\/a>, using my own custom schema.  This whole ordeal is for a couple of reasons...I wanted to learn about openLDAP and how schema works, and I wanted to eventually create something akin to &quot;Active Directory&quot; from my home that wasn't just &quot;use Samba&quot; or &quot;use FreeIPA.&quot;  I don't have anything against Samba, but it feels like using Samba is trying to acheive Microsoft functionality with Microsoft compatibility, and I don't need Microsoft compatibility, so I wanted to do it without.  I have no Microsoft devices in my ecosystem and have no plans to add any.  I don't have anything against FreeIPA either, but their docs target rpm distros and I tend to live in deb land, and I found the initial install frustrating unless I switched platforms...it felt like lock-in.  There are other solutions and this is a problem that has been solved other ways but with shortcomings, but I wanted to really do something &quot;from scratch&quot; so I share this not as a recommendation, but as a start-to-finish resource with some nuggets of wisdom that may help someone else whose journey brushes up against parts of mine.<\/p>\n<p>When I started, I was just authenticating web services like nextcloud, jellyfin, SAML, etc.  Tying those into openLDAP was very easy and has worked very well for a long time.  Now though, I want to use Kerberos for Linux PAM authentication as I am about to spin up a bunch of small servers (I got a new hypervisor and am redoing a lot of infrastructure, I don't want different credential stores all over the place).  Because of this, I'm bolting on MIT Kerberos as my network authentication provider; that being said, getting OpenLDAP and Kerberos to work together and only use 1 password was not intuitive.  Kerberos will use LDAP as it's database, and authentication will happen through SASL.  Ultimately, requests will go to openLDAP, then depending on the account, the password will either be validated in OpenLDAP for web users, or it will defer to SASL for users doing both web\/PAM stuff, which will then authenticate against the Kerberos passoword it stores in LDAP.  Circular much?<\/p>\n<p>Here is the vision, to help you see what I'm working towards.  Ultimately I have about 20 users, all of which might access web services like nextcloud, but 4 core users which will actually log into computers.  One of those 4 (me) has sudo rights on any computer.  All 20 users should be able to log into nextcloud, jellyfin, etc without issue, and for now I'm managing password resets if they come to my house (I had previously used phpldapadmin for password resets, but that was struggling on php7+ when I last tried).  For the internal users, they will be able to sit at ANY computer and log in using their same username and password.  If an web only user tries to sit and log into a computer, the'll get username not found.  If any of the 3 non-sudo users tries sudo, they'll get an unauthorized error.  If my account tries sudo, I can become root.  I can handle password resets for the internal users via kpasswd or kadmin.local.<\/p>\n<p><!--more--><\/p>\n<p>I don't know if this is a great solution, however it has been a great learning experience and it is working.  The one piece that is not clean is resetting passwords.  Because some passwords are stored in LDAP and some are stored in kerberos, I have to know where to change\/update\/reset them.  Most accounts I use Apache Directory Studio as an analogue to ADUC, but for my nice shiny Kerberos accounts, I have to use kpasswd on the command line.  This is not a great operational procedure; if there was a decent GUI\/webapp I could see making all accounts kerberos accounts.  I think this is solvable, but I'm stopping here because I want to move on to other priorities, and I have a workable solution.<\/p>\n<p>I'm including all my various final config files below so that the final working state can be documented for anyone else attempting to implement a solution like this.  When I was done, I burned the host to the ground and rebuilt it from this documentation to make sure it was correct.  Domain, subdomain, and host names are changed of course and passwords are redacted.  I used my name &quot;surfrock66&quot; as the prototype account, but you could replace with yours.  Honestly, if you did a find-replace of &quot;subdomain&quot;, then &quot;domain&quot;, then &quot;ldapserverhostname&quot;, then &quot;surfrock66&quot;, most of this would be copy\/paste.  I don't recommend that; this isn't meant to be a drop in, this is a learning journey that someone could follow to learn and recreate.<\/p>\n<p>Some accounts are created with the ldif files below, and in my personal case I've got the hashed passwords already in the files.  If you're copying this, you should generate all the passwords offline, run the ldif commands to create the account, then use Apache Directory Studio (connecting as admin) to actually change password as needed.  You should pregenerate the following passwords:<\/p>\n<ul>\n<li>admin (cn=admin,dc=subdomain,dc=domain,dc=com and admin@SUBDOMAIN.DOMAIN.COM) (used for both slapd admin AND kerberos admin)<\/li>\n<li>config admin (cn=admin,cn=config)<\/li>\n<li>KDC Master Key<\/li>\n<li>Your user account, replace surfrock66 (cn=surfrock66,ou=accounts,dc=subdomain,dc=domain,dc=com)<\/li>\n<li>kdc-service (cn=kdc-service,ou=accounts,dc=subdomain,dc=domain,dc=com)<\/li>\n<li>kadmin-service (cn=kadmin-service,ou=accounts,dc=subdomain,dc=domain,dc=com)<\/li>\n<li>ldapbinduser (cn=ldapbinduser,ou=accounts,dc=subdomain,dc=domain,dc=com) (This password will be put in plain text in places that store bind credentials in config files, as an FYI.)<\/li>\n<\/ul>\n<p>The commands to install stuff start here; I put a dpkg-reconfigure after to answer the prompts for any given package install.  If you're not prompted, you need to run through them:<\/p>\n<pre><code>apt -y update\napt -y install db-util db5.3-util krb5-admin-server krb5-config krb5-kdc krb5-kdc-ldap krb5-user ldap-utils libgssrpc4 libkadm5clnt-mit12 libkadm5srv-mit12 libkdb5-10 libltdl7 libodbc2 libsasl2-modules-gssapi-mit libverto-libevent1 libverto1 sasl2-bin schema2ldif slapd\n\ndpkg-reconfigure slapd\n# Don&#039;t omit initial configuration\n# Domain = subdomain.domain.com\n# Organization = subdomain.domain.com\n# Set an admin password\n# Do you want the DB removed when SlapD is purged...yes\n\ndpkg-reconfigure krb5-config\n# Default Kerberos Realm is SUBDOMAIN.DOMAIN.COM\n# Primary KDC is fqdn\n# Administrative server is fqdn\n\ndpkg-reconfigure krb5-admin-server\n# Ok\n\ndpkg-reconfigure krb5-kdc\n# No, don&#039;t create KDC config manually\n\nmkdir \/var\/log\/kerberos\ntouch \/var\/log\/kerberos\/krb5kdc.log\ntouch \/etc\/krb5kdc\/service.keyfile\nvi \/usr\/lib\/systemd\/system\/krb5-kdc.service\n# Add &quot;\/var\/log\/kerberos&quot; to the line &quot;ReadWriteDirectories&quot;\n\nvi \/usr\/lib\/systemd\/system\/krb5-admin-server.service\n# Add &quot;\/var\/log\/kerberos&quot; to the line &quot;ReadWriteDirectories&quot;\n\nsystemctl daemon-reload\nadduser openldap sasl<\/code><\/pre>\n<p>If you want to use an SSL cert to do TLS, you'll need to set that up.  On ubuntu, there's some weird permission stuff.  At a high level, we're creating an ssl-certs group, setting the cert file permissions to permit group reading, then adding the openldap service's account to that group; you can use whatever gid you want.  I personally have an easy-rsa CA, so I have an internal CA and domain issued cert\/key pair.  That may be too much for some people, which is fine, you would then omit the 2 TLS ldif files below.<\/p>\n<pre><code>groupadd -g 901 ssl-cert\nusermod -a -G ssl-cert openldap\n# Create the CA Cert File\n\/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem\n# Create the Certificate Private Key File\n\/etc\/ssl\/private\/ldap.subdomain.domain.com.key\n# Create the Certificate File\n\/etc\/ssl\/certs\/ldap.subdomain.domain.com.crt\nchown root:ssl-cert \/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem\nchown root:ssl-cert \/etc\/ssl\/private\nchown root:ssl-cert \/etc\/ssl\/private\/ldapserverhostname.subdomain.domain.com.key\nchown root:ssl-cert \/etc\/ssl\/certs\/ldapserverhostname.subdomain.domain.com.crt\nchmod 710 \/etc\/ssl\/private<\/code><\/pre>\n<p>First, here are all of the ldif files that represent the configuration of slapd and the ldap environment.  These are all of them as I set them up.  Note there are 2 types of ldapadd commands, some require authentication of the admin account, some don't.  I pasted them exactly as I successfully ran them.  One thing to consider is that you can't really just paste a password in for a lot of these accounts.  If you put nonsense in the password field, you could later open the object in Apache Directory Studio and set the password from the admin account, where it'll add it encrypted.  Once you're done, you can then use ADS to export the object as an ldif, so it's easy to recreate later (which is how I got most of these ldif's at first).<\/p>\n<p>01.config.InstallMemberOf.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 01.config.InstallMemberOf.ldif)<\/p>\n<pre><code>dn: cn=module{0},cn=config\nchangetype: modify\nadd: olcModuleLoad\nolcModuleLoad: memberof.la<\/code><\/pre>\n<p>02.schema.attribute.sshPublicKey.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 02.schema.attribute.sshPublicKey.ldif)<\/p>\n<pre><code>dn: cn=sshPublicKey,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: attribute.sshPublicKey\n#\n# LDAP Public Key Patch schema for use with openssh-ldappubkey\n#                              useful with PKA-LDAP also\n#\n# Adjusted: Dennis Leeuw &lt;dleeuw@made-it.com&gt;\n#           Making the uid a MUST, but the sshPublicKey a MAY\n#           so we can add the objectClass and later add the key\n#\n# Author: Eric AUGE &lt;eau@phear.org&gt;\n# \n# Based on the proposal of : Mark Ruijter\n#\n# octetString SYNTAX\nolcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME &#039;sshPublicKey&#039; \n  DESC &#039;MANDATORY: OpenSSH Public key&#039;\n  EQUALITY octetStringMatch\n  SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )\n# printableString SYNTAX yes|no\nolcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME &#039;ldapPublicKey&#039; SUP top AUXILIARY\n  DESC &#039;MANDATORY: OpenSSH LPK objectclass&#039;\n  MUST uid\n  MAY sshPublicKey\n  )<\/code><\/pre>\n<p>03.schema.sudo.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 03.schema.sudo.ldif)<\/p>\n<pre><code>dn: cn=sudo,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: sudo\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.1 NAME &#039;sudoUser&#039; DESC &#039;User(s) who may  run sudo&#039; EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.2 NAME &#039;sudoHost&#039; DESC &#039;Host(s) who may run sudo&#039; EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.3 NAME &#039;sudoCommand&#039; DESC &#039;Command(s) to be executed by sudo&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.4 NAME &#039;sudoRunAs&#039; DESC &#039;User(s) impersonated by sudo (deprecated)&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.5 NAME &#039;sudoOption&#039; DESC &#039;Options(s) followed by sudo&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.6 NAME &#039;sudoRunAsUser&#039; DESC &#039;User(s) impersonated by sudo&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: ( 1.3.6.1.4.1.15953.9.1.7 NAME &#039;sudoRunAsGroup&#039; DESC &#039;Group(s) impersonated by sudo&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcObjectClasses: ( 1.3.6.1.4.1.15953.9.2.1 NAME &#039;sudoRole&#039; SUP top STRUCTURAL DESC &#039;Sudoer Entries&#039; MUST ( cn ) MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ description ) )<\/code><\/pre>\n<p>04.schema.objectClass.domainAccount.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 04.schema.objectClass.domainAccount.ldif)<\/p>\n<pre><code>dn: cn=objectClass.domainAccount,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: objectClass.domainAccount\nolcObjectClasses: ( 1.3.6.1.4.1.57470.2.2.1 NAME &#039;domainAccount&#039;\n  DESC &#039;A user\/account\/person in the organization&#039;\n  SUP top STRUCTURAL\n  MUST ( cn $ name $ sn $ uid )\n  MAY ( \n  audio $   businessCategory $ carLicense $ departmentNumber $ \n  description $ destinationIndicator $ displayName $ \n  employeeNumber $ employeeType $ facsimileTelephoneNumber $ \n  gecos $ gidNumber $ givenName $ homeDirectory $ homePhone $ \n  homePostalAddress $ initials $ internationaliSDNNumber $ \n  jpegPhoto $ l $ labeledURI $ loginShell $ \n  mail $ manager $ mobile $ o $ ou $ pager $ photo $ \n  physicalDeliveryOfficeName $ postalAddress $ postalCode $ \n  postOfficeBox $ preferredDeliveryMethod $ \n  preferredLanguage $ registeredAddress $ roomNumber $ \n  secretary $ seeAlso $ shadowExpire $ shadowInactive $ \n  shadowLastChange $ shadowMax $ shadowMin $ shadowWarning $ \n  sshPublicKey $ st $ street $ telephoneNumber $ \n  teletexTerminalIdentifier $ telexNumber $ title $ \n  uidNumber $ userCertificate $ userPassword $ userPKCS12 $ \n  userSMIMECertificate $ x121Address $ x500uniqueIdentifier ) )<\/code><\/pre>\n<p>05.schema.objectClass.domainGroup.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 05.schema.objectClass.domainGroup.ldif)<\/p>\n<pre><code>dn: cn=objectClass.domainGroup,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: objectClass.domainGroup\nolcObjectClasses: ( 1.3.6.1.4.1.57470.2.2.2 NAME &#039;domainGroup&#039;\n  DESC &#039;A group of names in the organization&#039;\n  SUP top STRUCTURAL\n  MUST ( cn )\n  MAY ( \n  businessCategory $ description $ gidNumber $ member $\n  o $ ou $ owner $ seeAlso ) )<\/code><\/pre>\n<p>06.config.EnableMemberOf.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 06.config.EnableMemberOf.ldif)<\/p>\n<p>We're configuring 2 things here, changing the memberof overlay to use &quot;domainGroup&quot; instead of the default objectClass &quot;groupOfNames&quot; and enabling referential integrity.<\/p>\n<pre><code>dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config\nobjectClass: olcOverlayConfig\nobjectClass: olcMemberOf\nolcOverlay: memberof\nolcMemberOfRefint: TRUE\nolcMemberOfGroupOC: domainGroup<\/code><\/pre>\n<p>07.ous.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 07.ous.ldif)<\/p>\n<p>You need to authenticate with the admin account for this<\/p>\n<pre><code>dn: ou=accounts,dc=subdomain,dc=domain,dc=com\nobjectClass: organizationalUnit\nou: accounts\n\ndn: ou=groups,dc=subdomain,dc=domain,dc=com\nobjectClass: organizationalUnit\nou: groups\n\ndn: ou=hosts,dc=subdomain,dc=domain,dc=com\nobjectClass: organizationalUnit\nou: hosts<\/code><\/pre>\n<p>08.user.ldapbinduser.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 08.user.ldapbinduser.ldif)<\/p>\n<p>You need to authenticate with the admin account for this. (Password obviously redacted)<\/p>\n<pre><code>version: 1\n\ndn: cn=ldapbinduser,ou=accounts,dc=subdomain,dc=domain,dc=com\ncn: ldapbinduser\ndescription: LDAP Bind User\ndisplayname: ldapbinduser\ngecos: ldapbinduser\ngidnumber: 20000\nhomedirectory: \/nonexistent\nloginshell: No Login\nname: ldapbinduser\nobjectclass: domainAccount\nobjectclass: top\nou: ou=accounts,dc=subdomain,dc=domain,dc=com\nsn: ldapbinduser\nuid: ldapbinduser\nuidnumber: 2999\nuserpassword: {MD5}Redacted==<\/code><\/pre>\n<p>09.user.kdc-service.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 09.user.kdc-service.ldif)<\/p>\n<p>You need to authenticate with the admin account for this. (Password obviously redacted)<\/p>\n<pre><code>version: 1\n\ndn: cn=kdc-service,ou=accounts,dc=subdomain,dc=domain,dc=com\nobjectClass: domainAccount\nobjectClass: simpleSecurityObject\nobjectClass: top\ncn: kdc-service\nname: kdc-service\nsn: kdc-service\nuid: kdc-service\nuserPassword:: Redacted=\ndescription: Account used for the Kerberos KDC<\/code><\/pre>\n<p>10.user.kadmin-service.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 10.user.kadmin-service.ldif)<\/p>\n<p>You need to authenticate with the admin account for this. (Password obviously redacted)<\/p>\n<pre><code>version: 1\n\ndn: cn=kadmin-service,ou=accounts,dc=subdomain,dc=domain,dc=com\nobjectClass: domainAccount\nobjectClass: simpleSecurityObject\nobjectClass: top\ncn: kadmin-service\nname: kadmin-service\nsn: kadmin-service\nuid: kadmin-service\nuserPassword:: Redacted=\ndescription: Account used for the Kerberos Admin server<\/code><\/pre>\n<p>11.user.surfrock66.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 11.user.surfrock66.ldif) <\/p>\n<p>You need to authenticate with the admin account for this. (Password obviously redacted) You should replace this with your user; also this account is baked into future ldif files, so change it there too.  Update the info in this one with whatever personal info you want.<\/p>\n<pre><code>version: 1\n\ndn: cn=surfrock66,ou=accounts,dc=subdomain,dc=domain,dc=com\ncn: surfrock66\ndisplayname: surfrock66\ngecos: surfrock66\ngidnumber: 1000\ngivenname: Redacted\nhomedirectory: \/home\/surfrock66\nhomepostaladdress:: Redacted==\ninitials: Redacted\nl: Redacted\nlabeleduri: Redacted\nloginshell: \/bin\/bash\nmail: Redacted\nmobile: Redacted\nname: surfrock66\no: Redacted\nobjectclass: domainAccount\nobjectclass: top\nou: ou=accounts,dc=subdomain,dc=domain,dc=com\npostalcode: Redacted\npreferredlanguage: English\nsn: Gullo\nsshpublickey: ssh-rsa ---== surfrock66@openLDAP\nst: Redacted\nstreet: Redacted\ntelephonenumber: Redacted\nuid: surfrock66\nuidnumber: 1000\nuserpassword: {MD5}Redacted==<\/code><\/pre>\n<p>12.access.permissions.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 12.access.permissions.ldif)<\/p>\n<p>Make sure to replace your account name in here.<\/p>\n<pre><code>dn: olcDatabase={1}mdb,cn=config\nchangetype: modify\nreplace: olcAccess\nolcAccess: {0}to *\n  by dn=&quot;cn=admin,dc=subdomain,dc=domain,dc=com&quot; manage\n  by dn=&quot;cn=surfrock66,ou=accounts,dc=subdomain,dc=domain,dc=com&quot; manage\n  by dn=&quot;cn=ldapbinduser,ou=accounts,dc=subdomain,dc=domain,dc=com&quot; read\n  by dn=&quot;cn=kdc-service,ou=accounts,dc=subdomain,dc=domain,dc=com&quot; read\n  by dn=&quot;cn=kadmin-service,ou=accounts,dc=subdomain,dc=domain,dc=com&quot; write\n  by * break\n-\nadd: olcAccess\nolcAccess: {1}to dn.children=&quot;ou=accounts,dc=subdomain,dc=domain,dc=com&quot; attrs=userPassword,shadowExpire,shadowInactive,shadowLastChange,shadowMax,shadowMin,shadowWarning\n  by self write\n  by anonymous auth\n-\nadd: olcAccess\nolcAccess: {2}to dn.subtree=&quot;dc=subdomain,dc=domain,dc=com&quot;\n  by self read<\/code><\/pre>\n<p>13.config.defineCACert.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 13.config.defineCACert.ldif) <\/p>\n<p>Don't modify the order; it has to be CA -&gt; Key -&gt; Cert<\/p>\n<pre><code>dn: cn=config\nchangetype: modify\nadd: olcTLSCACertificateFile\nolcTLSCACertificateFile: \/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem\n-\nadd: olcTLSCertificateKeyFile\nolcTLSCertificateKeyFile: \/etc\/ssl\/private\/ldapserverhostname.subdomain.domain.com.key\n-\nadd: olcTLSCertificateFile\nolcTLSCertificateFile: \/etc\/ssl\/certs\/ldapserverhostname.subdomain.domain.com.crt<\/code><\/pre>\n<p>14.config.forceTLS.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 14.config.forceTLS.ldif)<\/p>\n<pre><code>dn: olcDatabase={0}config,cn=config\nchangetype: modify\nadd: olcSecurity\nolcSecurity: ssf=71\n-\n\ndn: olcDatabase={1}mdb,cn=config\nchangetype: modify\nadd: olcSecurity\nolcSecurity: ssf=71<\/code><\/pre>\n<p>GOTTA RESTART SLAPD AFTER THIS ONE with 'systemctl restart slapd'<\/p>\n<p>15.config.setConfigAdminPass.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 15.config.setConfigAdminPass.ldif) <\/p>\n<p>(Password obviously redacted)<\/p>\n<pre><code>dn: cn=config\nchangetype: modify\n\ndn: olcDatabase={0}config,cn=config\nchangetype: modify\nadd: olcRootPW\nolcRootPW: {SSHA}REDACTED<\/code><\/pre>\n<p>16.config.kerberosSchema.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 16.config.kerberosSchema.ldif)<\/p>\n<pre><code>dn: cn=kerberos,cn=schema,cn=config\nobjectClass: olcSchemaConfig\ncn: kerberos\nolcAttributeTypes: {0}( 2.16.840.1.113719.1.301.4.1.1 NAME &#039;krbPrincipalName&#039;\n EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1\n .1466.115.121.1.26 )\nolcAttributeTypes: {1}( 1.2.840.113554.1.4.1.6.1 NAME &#039;krbCanonicalName&#039; EQUAL\n ITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466\n .115.121.1.26 SINGLE-VALUE )\nolcAttributeTypes: {2}( 2.16.840.1.113719.1.301.4.3.1 NAME &#039;krbPrincipalType&#039;\n EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {3}( 2.16.840.1.113719.1.301.4.5.1 NAME &#039;krbUPEnabled&#039; DESC\n  &#039;Boolean&#039; SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\nolcAttributeTypes: {4}( 2.16.840.1.113719.1.301.4.6.1 NAME &#039;krbPrincipalExpira\n tion&#039; EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SING\n LE-VALUE )\nolcAttributeTypes: {5}( 2.16.840.1.113719.1.301.4.8.1 NAME &#039;krbTicketFlags&#039; EQ\n UALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {6}( 2.16.840.1.113719.1.301.4.9.1 NAME &#039;krbMaxTicketLife&#039;\n EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {7}( 2.16.840.1.113719.1.301.4.10.1 NAME &#039;krbMaxRenewableAg\n e&#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {8}( 2.16.840.1.113719.1.301.4.14.1 NAME &#039;krbRealmReference\n s&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {9}( 2.16.840.1.113719.1.301.4.15.1 NAME &#039;krbLdapServers&#039; E\n QUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\nolcAttributeTypes: {10}( 2.16.840.1.113719.1.301.4.17.1 NAME &#039;krbKdcServers&#039; E\n QUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {11}( 2.16.840.1.113719.1.301.4.18.1 NAME &#039;krbPwdServers&#039; E\n QUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {12}( 2.16.840.1.113719.1.301.4.24.1 NAME &#039;krbHostServer&#039; E\n QUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: {13}( 2.16.840.1.113719.1.301.4.25.1 NAME &#039;krbSearchScope&#039;\n EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {14}( 2.16.840.1.113719.1.301.4.26.1 NAME &#039;krbPrincipalRefe\n rences&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12\n )\nolcAttributeTypes: {15}( 2.16.840.1.113719.1.301.4.28.1 NAME &#039;krbPrincNamingAt\n tr&#039; EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALU\n E )\nolcAttributeTypes: {16}( 2.16.840.1.113719.1.301.4.29.1 NAME &#039;krbAdmServers&#039; E\n QUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {17}( 2.16.840.1.113719.1.301.4.30.1 NAME &#039;krbMaxPwdLife&#039; E\n QUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {18}( 2.16.840.1.113719.1.301.4.31.1 NAME &#039;krbMinPwdLife&#039; E\n QUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {19}( 2.16.840.1.113719.1.301.4.32.1 NAME &#039;krbPwdMinDiffCha\n rs&#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {20}( 2.16.840.1.113719.1.301.4.33.1 NAME &#039;krbPwdMinLength&#039;\n  EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {21}( 2.16.840.1.113719.1.301.4.34.1 NAME &#039;krbPwdHistoryLen\n gth&#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE\n )\nolcAttributeTypes: {22}( 1.3.6.1.4.1.5322.21.2.1 NAME &#039;krbPwdMaxFailure&#039; EQUAL\n ITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {23}( 1.3.6.1.4.1.5322.21.2.2 NAME &#039;krbPwdFailureCountInter\n val&#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE\n )\nolcAttributeTypes: {24}( 1.3.6.1.4.1.5322.21.2.3 NAME &#039;krbPwdLockoutDuration&#039;\n EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {25}( 1.2.840.113554.1.4.1.6.2 NAME &#039;krbPwdAttributes&#039; EQUA\n LITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {26}( 1.2.840.113554.1.4.1.6.3 NAME &#039;krbPwdMaxLife&#039; EQUALIT\n Y integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {27}( 1.2.840.113554.1.4.1.6.4 NAME &#039;krbPwdMaxRenewableLife\n &#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\nolcAttributeTypes: {28}( 1.2.840.113554.1.4.1.6.5 NAME &#039;krbPwdAllowedKeysalts&#039;\n  EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALU\n E )\nolcAttributeTypes: {29}( 2.16.840.1.113719.1.301.4.36.1 NAME &#039;krbPwdPolicyRefe\n rence&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 S\n INGLE-VALUE )\nolcAttributeTypes: {30}( 2.16.840.1.113719.1.301.4.37.1 NAME &#039;krbPasswordExpir\n ation&#039; EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SIN\n GLE-VALUE )\nolcAttributeTypes: {31}( 2.16.840.1.113719.1.301.4.39.1 NAME &#039;krbPrincipalKey&#039;\n  EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )\nolcAttributeTypes: {32}( 2.16.840.1.113719.1.301.4.40.1 NAME &#039;krbTicketPolicyR\n eference&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.1\n 2 SINGLE-VALUE )\nolcAttributeTypes: {33}( 2.16.840.1.113719.1.301.4.41.1 NAME &#039;krbSubTrees&#039; EQU\n ALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {34}( 2.16.840.1.113719.1.301.4.42.1 NAME &#039;krbDefaultEncSal\n tTypes&#039; EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\nolcAttributeTypes: {35}( 2.16.840.1.113719.1.301.4.43.1 NAME &#039;krbSupportedEncS\n altTypes&#039; EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\nolcAttributeTypes: {36}( 2.16.840.1.113719.1.301.4.44.1 NAME &#039;krbPwdHistory&#039; E\n QUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )\nolcAttributeTypes: {37}( 2.16.840.1.113719.1.301.4.45.1 NAME &#039;krbLastPwdChange\n &#039; EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-V\n ALUE )\nolcAttributeTypes: {38}( 1.3.6.1.4.1.5322.21.2.5 NAME &#039;krbLastAdminUnlock&#039; EQU\n ALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE\n )\nolcAttributeTypes: {39}( 2.16.840.1.113719.1.301.4.46.1 NAME &#039;krbMKey&#039; EQUALIT\n Y octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )\nolcAttributeTypes: {40}( 2.16.840.1.113719.1.301.4.47.1 NAME &#039;krbPrincipalAlia\n ses&#039; EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\nolcAttributeTypes: {41}( 2.16.840.1.113719.1.301.4.48.1 NAME &#039;krbLastSuccessfu\n lAuth&#039; EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SIN\n GLE-VALUE )\nolcAttributeTypes: {42}( 2.16.840.1.113719.1.301.4.49.1 NAME &#039;krbLastFailedAut\n h&#039; EQUALITY generalizedTimeMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-\n VALUE )\nolcAttributeTypes: {43}( 2.16.840.1.113719.1.301.4.50.1 NAME &#039;krbLoginFailedCo\n unt&#039; EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE\n )\nolcAttributeTypes: {44}( 2.16.840.1.113719.1.301.4.51.1 NAME &#039;krbExtraData&#039; EQ\n UALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )\nolcAttributeTypes: {45}( 2.16.840.1.113719.1.301.4.52.1 NAME &#039;krbObjectReferen\n ces&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {46}( 2.16.840.1.113719.1.301.4.53.1 NAME &#039;krbPrincContaine\n rRef&#039; EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )\nolcAttributeTypes: {47}( 2.16.840.1.113730.3.8.15.2.1 NAME &#039;krbPrincipalAuthIn\n d&#039; EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )\nolcAttributeTypes: {48}( 1.3.6.1.4.1.5322.21.2.4 NAME &#039;krbAllowedToDelegateTo&#039;\n  EQUALITY caseExactIA5Match SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.\n 1.1466.115.121.1.26 )\nolcObjectClasses: {0}( 2.16.840.1.113719.1.301.6.1.1 NAME &#039;krbContainer&#039; SUP t\n op STRUCTURAL MUST cn )\nolcObjectClasses: {1}( 2.16.840.1.113719.1.301.6.2.1 NAME &#039;krbRealmContainer&#039;\n SUP top STRUCTURAL MUST cn MAY ( krbMKey $ krbUPEnabled $ krbSubTrees $ krbSe\n archScope $ krbLdapServers $ krbSupportedEncSaltTypes $ krbDefaultEncSaltType\n s $ krbTicketPolicyReference $ krbKdcServers $ krbPwdServers $ krbAdmServers\n $ krbPrincNamingAttr $ krbPwdPolicyReference $ krbPrincContainerRef ) )\nolcObjectClasses: {2}( 2.16.840.1.113719.1.301.6.3.1 NAME &#039;krbService&#039; SUP top\n  ABSTRACT MUST cn MAY ( krbHostServer $ krbRealmReferences ) )\nolcObjectClasses: {3}( 2.16.840.1.113719.1.301.6.4.1 NAME &#039;krbKdcService&#039; SUP\n krbService STRUCTURAL )\nolcObjectClasses: {4}( 2.16.840.1.113719.1.301.6.5.1 NAME &#039;krbPwdService&#039; SUP\n krbService STRUCTURAL )\nolcObjectClasses: {5}( 2.16.840.1.113719.1.301.6.8.1 NAME &#039;krbPrincipalAux&#039; SU\n P top AUXILIARY MAY ( krbPrincipalName $ krbCanonicalName $ krbUPEnabled $ kr\n bPrincipalKey $ krbTicketPolicyReference $ krbPrincipalExpiration $ krbPasswo\n rdExpiration $ krbPwdPolicyReference $ krbPrincipalType $ krbPwdHistory $ krb\n LastPwdChange $ krbLastAdminUnlock $ krbPrincipalAliases $ krbLastSuccessfulA\n uth $ krbLastFailedAuth $ krbLoginFailedCount $ krbExtraData $ krbAllowedToDe\n legateTo $ krbPrincipalAuthInd ) )\nolcObjectClasses: {6}( 2.16.840.1.113719.1.301.6.9.1 NAME &#039;krbPrincipal&#039; SUP t\n op STRUCTURAL MUST krbPrincipalName MAY krbObjectReferences )\nolcObjectClasses: {7}( 2.16.840.1.113719.1.301.6.11.1 NAME &#039;krbPrincRefAux&#039; SU\n P top AUXILIARY MAY krbPrincipalReferences )\nolcObjectClasses: {8}( 2.16.840.1.113719.1.301.6.13.1 NAME &#039;krbAdmService&#039; SUP\n  krbService STRUCTURAL )\nolcObjectClasses: {9}( 2.16.840.1.113719.1.301.6.14.1 NAME &#039;krbPwdPolicy&#039; SUP\n top STRUCTURAL MUST cn MAY ( krbMaxPwdLife $ krbMinPwdLife $ krbPwdMinDiffCha\n rs $ krbPwdMinLength $ krbPwdHistoryLength $ krbPwdMaxFailure $ krbPwdFailure\n CountInterval $ krbPwdLockoutDuration $ krbPwdAttributes $ krbPwdMaxLife $ kr\n bPwdMaxRenewableLife $ krbPwdAllowedKeysalts ) )\nolcObjectClasses: {10}( 2.16.840.1.113719.1.301.6.16.1 NAME &#039;krbTicketPolicyAu\n x&#039; SUP top AUXILIARY MAY ( krbTicketFlags $ krbMaxTicketLife $ krbMaxRenewabl\n eAge ) )\nolcObjectClasses: {11}( 2.16.840.1.113719.1.301.6.17.1 NAME &#039;krbTicketPolicy&#039;\n SUP top STRUCTURAL MUST cn )<\/code><\/pre>\n<p>17.config.SASLConfig.ldif (ldapadd -Y EXTERNAL -H ldapi:\/\/\/ -f 17.config.SASLConfig.ldif)<\/p>\n<pre><code>dn: cn=config\nchangetype: modify\nadd: olcSaslHost\nolcSaslHost: hostname.subdomain.domain.com\n-\nadd: olcSaslRealm\nolcSaslRealm: SUBDOMAIN.DOMAIN.COM\n-\nadd: olcAuthzRegexp\nolcAuthzRegexp: {0}&quot;cn=([^\/]*),cn=subdomain.domain.com,cn=GSSAPI,cn=auth&quot; &quot;cn=$1,ou=accounts,dc=subdomain,dc=domain,dc=com&quot;\n-\nadd: olcAuthzRegexp\nolcAuthzRegexp: {1}&quot;cn=host\/([^\/]*).subdomain.domain.com,cn=subdomain.domain.com,cn=GSSAPI,cn=auth&quot; &quot;cn=$1,ou=hosts,dc=subdomain,dc=domain,dc=com&quot;\n-\nadd: olcAuthzRegexp\nolcAuthzRegexp: {2}&quot;uid=ldap\/admin,cn=subdomain.domain.com,cn=GSSAPI,cn=auth&quot; &quot;cn=admin,dc=subdomain,dc=domain,dc=com&quot;<\/code><\/pre>\n<p>18.ou.sudoers.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 18.ou.sudoers.ldif)<\/p>\n<pre><code>dn: ou=SUDOers,dc=subdomain,dc=domain,dc=com\nobjectclass: organizationalunit\nou: SUDOers\ndescription: Users who can use Sudo<\/code><\/pre>\n<p>19.sudorole.default.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 19.sudorole.default.ldif)<\/p>\n<p>These are default sudo attributes given to all sudo users<\/p>\n<pre><code>dn: cn=defaults,ou=SUDOers,dc=subdomain,dc=domain,dc=com\nobjectClass: top\nobjectClass: sudoRole\ncn: defaults\ndescription: Default Sudo Options\nsudoOption: !visiblepw\nsudoOption: always_set_home\nsudoOption: match_group_by_gid\nsudoOption: always_query_group_plugin\nsudoOption: env_reset\nsudoOption: env_keep =  &quot;COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS&quot;\nsudoOption: env_keep += &quot;MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE&quot;\nsudoOption: env_keep += &quot;LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES&quot;\nsudoOption: env_keep += &quot;LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE&quot;\nsudoOption: env_keep += &quot;LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY&quot;\nsudoOption: env_keep+=SSH_AUTH_SOCK\nsudoOption: secure_path = \/sbin:\/bin:\/usr\/sbin:\/usr\/bin<\/code><\/pre>\n<p>20.sudorole.sudo.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 20.sudorole.sudo.ldif)<\/p>\n<p>This is the normal sudo role, where the user is basically root<\/p>\n<pre><code>dn: cn=sudo,ou=SUDOers,dc=subdomain,dc=domain,dc=com\nobjectClass: top\nobjectClass: sudoRole\ncn: sudo\nsudoHost: ALL\nsudoRunAsUser: ALL\nsudoCommand: ALL<\/code><\/pre>\n<p>21.sudouser.surfrock66.ldif (ldapadd -x -D 'cn=admin,dc=subdomain,dc=domain,dc=com' -W -H ldapi:\/\/\/ -f 21.sudouser.surfrock66.ldif)<\/p>\n<p>This is how you add a user to sudo for all joined devices.  At the end of this post is a link to an article on adding more nuanced sudo roles and permissions, but this is your template for adding a user to sudo.<\/p>\n<pre><code>dn: cn=sudo,ou=SUDOers,dc=subdomain,dc=domain,dc=com\nchangetype: modify\nadd: sudoUser\nsudoUser: surfrock66<\/code><\/pre>\n<p>Now we get into configuration files. Here is just about everything for OpenLDAP:<\/p>\n<p>cat \/etc\/default\/slapd | grep -v -e &quot;#&quot; -e &quot;^[[:space:]]*$&quot;<\/p>\n<pre><code>SLAPD_CONF=\nSLAPD_USER=&quot;openldap&quot;\nSLAPD_GROUP=&quot;openldap&quot;\nSLAPD_PIDFILE=\nSLAPD_SERVICES=&quot;ldap:\/\/\/ ldapi:\/\/\/&quot;\nSLAPD_SENTINEL_FILE=\/etc\/ldap\/noslapd\nexport KRB5_KTNAME=&quot;FILE:\/etc\/ldap\/kerberos.ldap.ldapserverhostname.subdomain.domain.com.keytab&quot;\nSLAPD_OPTIONS=&quot;&quot;<\/code><\/pre>\n<p>cat \/etc\/ldap\/ldap.conf | grep -v -e &quot;#&quot; -e &quot;^[[:space:]]*$&quot;<\/p>\n<pre><code>TLS_CACERT      \/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.YY.pem\nSASL_MECH       GSSAPI\nSASL_REALM      SUBDOMAIN.DOMAIN.COM<\/code><\/pre>\n<p>cat \/usr\/lib\/sasl2\/slapd.conf  | grep -v -e &quot;#&quot; -e &quot;^[[:space:]]*$&quot;<\/p>\n<pre><code>mech_list: plain\npwcheck_method: saslauthd\nsaslauthd_path: \/var\/run\/saslauthd\/mux<\/code><\/pre>\n<p>Here is the config for saslauthd:<\/p>\n<p>cat \/etc\/default\/saslauthd | grep -v -e &quot;#&quot; -e &quot;^[[:space:]]*$&quot;<\/p>\n<pre><code>START=yes\nKRB5_TRACE=&quot;\/dev\/stderr&quot;\nDESC=&quot;SASL Authentication Daemon&quot;\nNAME=&quot;saslauthd&quot;\nMECHANISMS=&quot;kerberos5&quot;\nMECH_OPTIONS=&quot;&quot;\nTHREADS=5\nOPTIONS=&quot;-c -m \/var\/run\/saslauthd&quot;<\/code><\/pre>\n<p>cat \/etc\/krb5.conf | grep -v -e &quot;#&quot; -e &quot;^[[:space:]]*$&quot; <\/p>\n<pre><code>[libdefaults]\n    default_realm = SUBDOMAIN.DOMAIN.COM\n    kdc_timesync = 1\n    ccache_type = 4\n    forwardable = true\n    proxiable = true\n    fcc-mit-ticketflags = true\n[logging]\n    kdc = SYSLOG:DEBUG\n    admin_server = SYSLOG:DEBUG\n    default = SYSLOG:DEBUG\n    kdc = FILE:\/var\/log\/kerberos\/krb5kdc.log\n    admin_server = FILE:\/var\/log\/kerberos\/kadmin.log\n    default = FILE:\/var\/log\/kerberos\/krb5lib.log\n[realms]\n    SUBDOMAIN.DOMAIN.COM = {\n        kdc = ldapserverhostname.subdomain.domain.com\n        admin_server = ldapserverhostname.subdomain.domain.com\n        default_domain = subdomain.domain.com\n        database_module = openldap_ldapconf\n    }\n[domain_realm]\n    .subdomain.domain.com = SUBDOMAIN.DOMAIN.COM\n    subdomain.domain.com = SUBDOMAIN.DOMAIN.COM\n[dbdefaults]\n    ldap_kerberos_container_dn = cn=krbContainer,dc=subdomain,dc=domain,dc=com\n[dbmodules]\n    openldap_ldapconf = {\n        db_library = kldap\n        disable_last_success = true\n        disable_lockout  = true\n        acl_file = \/etc\/krb5kdc\/kadm5.acl\n        key_stash_file = \/etc\/krb5kdc\/stash\n        ldap_kdc_dn = &quot;cn=kdc-service,ou=accounts,dc=subdomain,dc=domain,dc=com&quot;\n        ldap_kadmind_dn = &quot;cn=kadmin-service,ou=accounts,dc=subdomain,dc=domain,dc=com&quot;\n        ldap_service_password_file = \/etc\/krb5kdc\/service.keyfile\n        ldap_servers = ldapi:\/\/\/\n        ldap_conns_per_server = 5\n        }<\/code><\/pre>\n<p>cat \/etc\/krb5kdc\/kadm5.acl (My user will be an administrator, hence adding it with all privs):<\/p>\n<pre><code>*\/admin@SUBDOMAIN.DOMAIN.COM *\nsurfrock66@SUBDOMAIN.DOMAIN.COM *<\/code><\/pre>\n<p>cat \/etc\/rsyslog.d\/51-slapd.conf # Direct logs to \/var\/log\/slapd.log<\/p>\n<pre><code>local4.* \/var\/log\/slapd.log<\/code><\/pre>\n<p>Relevant Bind9 DNS entries in the home domain's zone:<\/p>\n<pre><code>; Kerberos Records\n_kerberos._tcp          SRV     0       0       88      ldapserverhostname\n_kerberos._udp          SRV     0       0       88      ldapserverhostname\n_kerberos-master._tcp   SRV     0       0       88      ldapserverhostname\n_kerberos-master._udp   SRV     0       0       88      ldapserverhostname\n_kerberos-adm._tcp      SRV     0       0       749     ldapserverhostname\n_kpasswd._udp           SRV     0       0       464     ldapserverhostname<\/code><\/pre>\n<p>If UFW is in play you need to open the required firewall ports:<\/p>\n<pre><code>ufw allow 21\/tcp # Kerberos ftp uses the default port\nufw allow 23\/tcp # Kerberos telnet uses the default port\nufw allow 88\/udp # Kerberos V5 KDC\nufw allow 88\/tcp # Kerberos V5 KDC\nufw allow 464\/udp # Kerberos V5 Kpasswd\nufw allow 464\/tcp # Kerberos V5 Kpasswd\nufw allow 543\/tcp # Kerberos authenticated rlogin\nufw allow 544\/tcp # and remote shell\nufw allow 749\/tcp # Kerberos 5 admin\/changepw\nufw allow 749\/udp # Kerberos 5 admin\/changepw\nufw allow 754\/tcp # Kerberos slave propagation\nufw allow 2105\/tcp # Kerberos auth. &amp; encrypted rlogin\nufw allow 4444\/tcp # Kerberos 5 to 4 ticket translator<\/code><\/pre>\n<p>And now, the operational nonsense of running this.  You should reboot at this point, then do the following commands; make sure to generate and store very strong passwords for each step when asked:<\/p>\n<p>Create the realm:<\/p>\n<pre><code># You need the LDAP admin password, and a stront password for the KDC master key\nkdb5_ldap_util -D cn=admin,dc=subdomain,dc=domain,dc=com -H ldapi:\/\/\/ create -subtrees dc=subdomain,dc=domain,dc=com -sscope SUB -r SUBDOMAIN.DOMAIN.COM<\/code><\/pre>\n<p>Stash the passwords for binding:<\/p>\n<pre><code># kdc-service LDAP Password\nkdb5_ldap_util -D cn=admin,dc=subdomain,dc=domain,dc=com stashsrvpw -f \/etc\/krb5kdc\/service.keyfile cn=kdc-service,ou=accounts,dc=subdomain,dc=domain,dc=com\n# kadmin-service LDAP Password\nkdb5_ldap_util -D cn=admin,dc=subdomain,dc=domain,dc=com stashsrvpw -f \/etc\/krb5kdc\/service.keyfile cn=kadmin-service,ou=accounts,dc=subdomain,dc=domain,dc=com\n# KDC Master Key\nkdb5_util stash -f \/etc\/krb5kdc\/stash<\/code><\/pre>\n<p>Restart the kerberos services:<\/p>\n<pre><code>systemctl restart krb5-kdc\nsystemctl restart krb5-admin-server<\/code><\/pre>\n<p>Enter the kadmin service and start creating and managing Kerberos Principals:<\/p>\n<pre><code>kadmin.local\n\n# Take control of kadmin\/admin@SUBDOMAIN.DOMAIN.COM and set the password to your LDAP admin password\ncpw kadmin\/admin\n\n# Create my user\naddprinc surfrock66\n\n# This is so saslauthd has access to kerberos\naddprinc -randkey ldap\/ldapserverhostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\nktadd -k \/etc\/ldap\/ldap.keytab ldap\/ldapserverhostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\n\naddprinc -randkey host\/ldapserverhostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\nktadd -k \/etc\/ldap\/ldap.keytab host\/ldapserverhostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\nktadd -k \/etc\/krb5.keytab host\/ldapserverhostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\n\nexit<\/code><\/pre>\n<p>Initialize host kerberos:<\/p>\n<pre><code>chown openldap:openldap \/etc\/ldap\/ldap.keytab\nchmod 640 \/etc\/ldap\/ldap.keytab\nkinit -k -t \/etc\/krb5.keytab\nsystemctl restart slapd krb5-kdc krb5-admin-server saslauthd<\/code><\/pre>\n<p>Now we can create our users, and shift their password to use kerberos and not LDAP.  First, I will create the user in Apache Directory Studio.  I will set a password there, for testing.  Once that's done, I will test the account from the ldap server, and another computer:<\/p>\n<p>From the LDAP server:<\/p>\n<pre><code>ldapsearch -x -D cn=testuser,ou=accounts,dc=subdomain,dc=domain,dc=com -W -b dc=subdomain,dc=domain,dc=com -H ldapi:\/\/\/<\/code><\/pre>\n<p>And from a different computer, where we use -ZZ to check starttls:<\/p>\n<pre><code>ldapsearch -ZZ -x -D cn=testuser,ou=accounts,dc=subdomain,dc=domain,dc=com -W -b dc=subdomain,dc=domain,dc=com -H ldap:\/\/ldapserverhostname.subdomain.domain.com<\/code><\/pre>\n<p>On the LDAP server, we get in with kadmin and create a new user principle; for this test, you should use a different password than the LDAP password to help verify how you are authenticating:<\/p>\n<pre><code>kadmin.local\naddprinc testuser<\/code><\/pre>\n<p>This will create a principle testuser@SUBDOMAIN.DOMAIN.COM.  Then, in ApacheDirectoryStudio, I change the user's password to exactly &quot;{SASL}testuser@SUBDOMAIN.DOMAIN.COM&quot; which is code to tell LDAP to forward the password attempt to kerberos via SASL.<\/p>\n<p>Change a kerberos password (You will need the old password)<\/p>\n<pre><code>kpasswd testuser<\/code><\/pre>\n<p>Or, you do it from kadmin on the ldap server:<\/p>\n<pre><code>kadmin.local\ncpw testuser<\/code><\/pre>\n<p>Now, to onboard a new computer, we do the following on the new client.  This is one big blast, so if it's confusing, take it apart and go line by line; the focus of this article is more on the server config, so the client config is fairly abridged; this is literally my copy\/paste from my onboarding script, there are lots of ways to automate this<\/p>\n<pre><code>apt update\napt -y install sssd-ldap sssd-krb5 ldap-utils krb5-user sssd-common sssd libpam-sss libnss-sss sssd-tools libsss-sudo\n# Default realm is SUBDOMAIN.DOMAIN.COM\n\n# Ensure the rootCA&#039;s cert, that validates the LDAP server, is \/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem or wherever you put it, just modify the sssd.conf below.  Make sure not to ignore the &quot;ldap_schema&quot; and &quot;ldap_group_member&quot; as they are required to make memberof work.  \n\nsed -i &quot;s|$(hostname)|$(hostname).subdomain.domain.com $(hostname)|g&quot; \/etc\/hosts\nhostnamectl set-hostname client-hostname.subdomain.domain.com\n\necho -e &quot;[sssd]&quot; &gt; \/etc\/sssd\/sssd.conf\necho -e &quot;config_file_version = 2&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;debug_level = 4&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;services = nss, pam, sudo&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;domains = subdomain.domain.com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;[sudo]&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;[domain\/subdomain.domain.com]&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;debug_level = 4&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;cache_credentials = True&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;enumerate = True&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;id_provider = ldap&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;sudo_provider = ldap&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_uri = ldap:\/\/ldapserverhostname.subdomain.domain.com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_search_base = dc=subdomain,dc=domain,dc=com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_group_search_base = ou=groups,dc=subdomain,dc=domain,dc=com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_sudo_search_base = ou=SUDOers,dc=subdomain,dc=domain,dc=com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_sudo_full_refresh_interval=86400&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_sudo_smart_refresh_interval=3600&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_schema = rfc2307bis&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_user_object_class = domainAccount&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_group_object_class = domainGroup&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_group_member = member&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_default_bind_dn = cn=ldapbinduser,ou=accounts,dc=subdomain,dc=domain,dc=com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_default_authtok = PLAINTEXTPASSFORBINDUSER&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_id_use_start_tls = True&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_tls_reqcert = demand&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_tls_cacert = \/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;ldap_tls_cacertdir = \/etc\/ssl\/certs&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;auth_provider = krb5&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;chpass_provider = krb5&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;krb5_server = ldapserverhostname.subdomain.domain.com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;krb5_kpasswd = ldapserverhostname.subdomain.domain.com&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;krb5_realm = SUBDOMAIN.DOMAIN.COM&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\necho -e &quot;create_homedir = True&quot; &gt;&gt; \/etc\/sssd\/sssd.conf\n\nchmod 600 \/etc\/sssd\/sssd.conf\n\necho -e &quot;[libdefaults]&quot; &gt; \/etc\/krb5.conf\necho -e &quot;tdefault_realm = SUBDOMAIN.DOMAIN.COM&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tkdc_timesync = 1&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tccache_type = 4&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tforwardable = true&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tproxiable = true&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tfcc-mit-ticketflags = true&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;[realms]&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tSUBDOMAIN.DOMAIN.COM = {&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;ttkdc = ldapserverhostname.subdomain.domain.com&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;ttadmin_server = ldapserverhostname.subdomain.domain.com&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;ttdefault_domain = subdomain.domain.com&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;t}&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;[domain_realm]&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;t.subdomain.domain.com = SUBDOMAIN.DOMAIN.COM&quot; &gt;&gt; \/etc\/krb5.conf\necho -e &quot;tsubdomain.domain.com = SUBDOMAIN.DOMAIN.COM&quot; &gt;&gt; \/etc\/krb5.conf\n\necho -e &quot;TLS_CACERTt\/etc\/ssl\/certs\/subdomain.domain.com.rootca.YYYY.MM.DD.pem&quot; &gt; \/etc\/ldap\/ldap.conf\n\necho -e &quot;passwd:ttfiles systemd sss&quot; &gt; \/etc\/nsswitch.conf\necho -e &quot;group:ttfiles systemd sss&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;shadow:ttfiles sss&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;gshadow:tfiles&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;sudoers:tfiles sss&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;hosts:ttfiles mdns4_minimal [NOTFOUND=return] dns&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;networks:tfiles&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;protocols:tdb files&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;services:tdb files sss&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;ethers:ttdb files&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;rpc:ttdb files&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;netgroup:tnis sss&quot; &gt;&gt; \/etc\/nsswitch.conf\necho -e &quot;automount:tsss&quot; &gt;&gt; \/etc\/nsswitch.conf\n\necho -e &quot;[Seat:*]&quot; &gt;&gt; \/etc\/lightdm\/lightdm.conf\necho -e &quot;greeter-show-manual-login=true&quot; &gt;&gt; \/etc\/lightdm\/lightdm.conf\n\nkadmin -p surfrock66@SUBDOMAIN.DOMAIN.COM\n# Login with password\n\naddprinc -randkey host\/client-hostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\n\nktadd -k \/etc\/krb5.keytab host\/client-hostname.subdomain.domain.com@SUBDOMAIN.DOMAIN.COM\nexit\n\nkinit -k -t \/etc\/krb5.keytab\nsystemctl restart sssd\nsystemctl restart lightdm\n\n# Test with an LDAP username\ngetent passwd username<\/code><\/pre>\n<p>Now, let's say you have an existing computer with an account, specifically one with that username\/uid combination.  This is ok; after setting this all up, you can go in and do &quot;userdel username&quot;.  When you try to login again, it will scoop up the account from LDAP\/Kerberos, and as long as the UID is the same, all file persmissions will persist.<\/p>\n<p>There are many more pieces here which I will now work to solve, but as it is, we're pretty much done.  I use syncthing to keep user directories in sync, which works well.  I want to tie ssh into ldap, which is supported, but realistically I'm the only ssh user right now and I already put in my keys when onboarding a new device, so it's not critical.  The last piece is a better user-interface for managing password changes and, to be blunt, directory info.  I could see a world where a system like this better emulates Active Directory or other directory services out there, but for the homelab, it's working great.<\/p>\n<p>Below are my references; there are certainly more as this has been a meandering journey over MANY MONTHS:<\/p>\n<ul>\n<li><a href=\"http:\/\/labs.opinsys.com\/blog\/2010\/03\/16\/openldap-authentication-with-kerberos-backend-using-sasl\/\">http:\/\/labs.opinsys.com\/blog\/2010\/03\/16\/openldap-authentication-with-kerberos-backend-using-sasl\/<\/a><\/li>\n<li><a href=\"https:\/\/www.openldap.org\/doc\/admin24\/security.html#Pass-Through%20authentication\">https:\/\/www.openldap.org\/doc\/admin24\/security.html#Pass-Through%20authentication<\/a><\/li>\n<li><a href=\"https:\/\/ltb-project.org\/documentation\/sasl_delegation.html\">https:\/\/ltb-project.org\/documentation\/sasl_delegation.html<\/a><\/li>\n<li><a href=\"https:\/\/ubuntu.com\/server\/docs\/service-sssd\">https:\/\/ubuntu.com\/server\/docs\/service-sssd<\/a><\/li>\n<li><a href=\"https:\/\/ubuntu.com\/server\/docs\/service-kerberos-with-openldap-backend\">https:\/\/ubuntu.com\/server\/docs\/service-kerberos-with-openldap-backend<\/a><\/li>\n<li><a href=\"https:\/\/unix.stackexchange.com\/questions\/486444\/how-to-tell-kerberos-where-to-find-the-root-certificate-for-ldap-with-tls\">https:\/\/unix.stackexchange.com\/questions\/486444\/how-to-tell-kerberos-where-to-find-the-root-certificate-for-ldap-with-tls<\/a><\/li>\n<li><a href=\"https:\/\/kifarunix.com\/how-to-configure-sudo-via-openldap-server\/\">https:\/\/kifarunix.com\/how-to-configure-sudo-via-openldap-server\/<\/a><\/li>\n<li><a href=\"https:\/\/gist.github.com\/mazgi\/3dbfe99fb2b3e8d1e50b\">https:\/\/gist.github.com\/mazgi\/3dbfe99fb2b3e8d1e50b<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>I previously posted my experiences setting up OpenLDAP on Ubuntu Server, using my own custom schema. This whole ordeal is for a couple of reasons&#8230;I wanted to learn about openLDAP and how schema works, and I wanted to eventually create something akin to &quot;Active Directory&quot; from my home that wasn&#8217;t just &quot;use Samba&quot; or &quot;use [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[],"class_list":["post-1712","post","type-post","status-publish","format-standard","hentry","category-geek"],"_links":{"self":[{"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/posts\/1712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/surfrock66.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1712"}],"version-history":[{"count":65,"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/posts\/1712\/revisions"}],"predecessor-version":[{"id":1787,"href":"https:\/\/surfrock66.com\/index.php?rest_route=\/wp\/v2\/posts\/1712\/revisions\/1787"}],"wp:attachment":[{"href":"https:\/\/surfrock66.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/surfrock66.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/surfrock66.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}