{"id":183,"date":"2020-08-17T13:00:00","date_gmt":"2020-08-17T13:00:00","guid":{"rendered":"http:\/\/10.171.10.16:10000\/?p=183"},"modified":"2022-01-16T17:15:16","modified_gmt":"2022-01-16T17:15:16","slug":"centos-kickstart-ad-joining","status":"publish","type":"post","link":"https:\/\/dannypayne.me\/?p=183","title":{"rendered":"CentOS kickstart: AD Joining"},"content":{"rendered":"\n<p>This is the forth (and probably final!) post in a series on building\/setting up\/improving a PXE boot server with CentOS.<br>By the end of this post, we&#8217;ll have modified our kickstart file to allow copying of scripts and files to automate the process of our VM joining Active Directory. We&#8217;ll also automate creation of an AD group to allow specific users <code>sudo<\/code> access on our VM. <br>Why would we want to join our VM to Active Directory? For a number of reasons! Joining an AD Domain allows us to authenticate with an AD account on the VM instead of using local accounts on the VM. It also allows us to control who has <code>sudo<\/code> access via AD security groups, which I&#8217;m going to cover in this post. <\/p>\n\n\n\n<p>A requirement for this to all work properly is of course a working Active Directory environment &#8211; we&#8217;ll need to have AD DS installed on a Windows Server VM, and DNS pointing towards our Domain Controller. We&#8217;ll also need an account within AD that is either a Domain Admin (not ideal) <em>or<\/em> a service account with delegated permissions to <strong>create computer objects<\/strong> and <strong>create group objects<\/strong> (ideal). <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating our AD service account<\/h3>\n\n\n\n<p>To get things started, we&#8217;re going to want to create our AD service account for joining our VM&#8217;s to AD. <br>In my example, I&#8217;m creating a user with the <strong>username<\/strong> and <strong>full name<\/strong> set to <code>LinuxADJoinService<\/code>. The user is created in the following location: <em>ad.goatfarm.co.uk &gt; Goatfarm Users &gt; Service accounts<\/em>. The users password is set to <strong>not expire<\/strong> and the <strong>user cannot change password<\/strong>. <br><strong>Make note of the users password, we&#8217;ll need that soon.<\/strong> I generally use <a href=\"https:\/\/passwordsgenerator.net\/\" target=\"_blank\" rel=\"noreferrer noopener\">passwordsgenerator.net<\/a> to generate passwords. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-16-17_47_01-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg\" alt=\"\" class=\"wp-image-185\" width=\"469\" height=\"231\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_47_01-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg 411w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_47_01-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01-300x147.jpg 300w\" sizes=\"auto, (max-width: 469px) 100vw, 469px\" \/><\/figure><\/div>\n\n\n\n<p>Next, we need to delegate control so the <code>LinuxADJoinService<\/code> account can create computer objects. Inside of ADUC, find or create your OU for where the VM&#8217;s computer objects will be placed, right-click it and select <strong>Delegate Control&#8230;<\/strong><br>Click <strong>Next<\/strong> on the first step of the wizard, and when prompted, click <strong>Add<\/strong> and find the account you just created. Hit <strong>Next<\/strong> again.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-16-17_51_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg\" alt=\"\" class=\"wp-image-186\" width=\"518\" height=\"162\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_51_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg 499w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_51_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01-300x94.jpg 300w\" sizes=\"auto, (max-width: 518px) 100vw, 518px\" \/><\/figure><\/div>\n\n\n\n<p>When prompted to select <strong>Tasks to Delegate<\/strong> select <strong>Create a custom task to delegate<\/strong> and click <strong>Next<\/strong>. <br>On the next page of the wizard, select <strong>Only the following objects in the folder<\/strong>, and then tick <strong>Computer Objects<\/strong>. At the bottom of the wizard, select <strong>Create selected objects in this folder<\/strong> and then click <strong>Next<\/strong>. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-16-17_54_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg\" alt=\"\" class=\"wp-image-187\" width=\"524\" height=\"412\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_54_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg 499w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_54_08-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01-300x236.jpg 300w\" sizes=\"auto, (max-width: 524px) 100vw, 524px\" \/><\/figure><\/div>\n\n\n\n<p>On the next page of the wizard, tick <strong>General<\/strong> and <strong>Creation\/deletion of specific child objects<\/strong>. Within the <strong>Permissions<\/strong> box, find and tick <strong>Create all Child Objects<\/strong> and click <strong>Next<\/strong>. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-16-17_57_35-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg\" alt=\"\" class=\"wp-image-188\" width=\"535\" height=\"420\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_57_35-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01.jpg 499w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-16-17_57_35-mRemoteNG-Homelab-MRemoteNG.xml-COLO-DC01-300x236.jpg 300w\" sizes=\"auto, (max-width: 535px) 100vw, 535px\" \/><\/figure><\/div>\n\n\n\n<p>Finally, click <strong>Finish<\/strong> and we&#8217;re done! We just need to do this one more time to allow creation of groups within our Security Groups OU &#8211; the process is the same, however instead of selecting <strong>Computer Objects<\/strong>, we select <strong>Group Objects<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Modifying our kickstart file<\/h3>\n\n\n\n<p>Time to SSH into our PXE server! Once again, we need to modify our kickstart file. This time, we need to add the required packages for domain joining and authentication to the <strong>packages<\/strong> section. <\/p>\n\n\n\n<pre title=\"\/var\/ftp\/centos7\/anaconda-ks.cfg\" class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">%packages\n@base\n@core\n...\n# Required for AD Authentication\nsssd\nrealmd\noddjob\noddjob-mkhomedir\nadcli\nsamba-common\nsamba-common-tools\nkrb5-workstation\nopenldap-clients\npolicycoreutils-python\n...\n%end<\/code><\/pre>\n\n\n\n<p>Next, we need to add a few lines to our <strong>post<\/strong> section to <code>wget<\/code> our domain join script. <br>In the previous post, we added a line to edit the root crontab &#8211; we need to remove this and replace it with another line to call our <code>domainjoin<\/code> script instead. The <code>buildcleanup<\/code> script will be added to the root crontab as part of the <code>domainjoin<\/code> script. Our <strong>post<\/strong> section should look something like the below:<\/p>\n\n\n\n<pre title=\"\/var\/ftp\/centos7\/anaconda-ks.cfg\" class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">\n%post --log=\/tmp\/post-build.log\n...\n#########################################\n# Copy our domain join script\nwget -O \/tmp\/build\/domainjoin.sh ftp:\/\/10.176.40.10\/centos7\/config\/domainjoin.sh\nchmod +x \/tmp\/build\/domainjoin.sh\necho \"@reboot sleep 60 &amp;&amp; \/tmp\/build\/domainjoin.sh &amp;&amp; \/sbin\/shutdown -r now\" > \/var\/spool\/cron\/root\n\n#########################################\n# Copy our build cleanup script\nwget -O \/tmp\/build\/buildcleanup.sh ftp:\/\/10.176.40.10\/centos7\/config\/buildcleanup.sh\nchmod +x \/tmp\/build\/buildcleanup.sh\n\n...\n%end\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create domain join script<\/h3>\n\n\n\n<p>We now need to create the domain joining script. <br>The script is basically pre-creating the computer account within AD in the location specified in the <code>domain-ou<\/code> option with the machine password specified by <code>one-time-password<\/code>, and then verifying the location of the pre-created computer object with the <code>computer-ou<\/code> option then completing the join. <br>We then copy the <code>sssd.conf<\/code> and <code>domainsudoers<\/code> files. After this the <code>domainsudoers<\/code> file gets edited via <code>sed<\/code> with an entry for the security group that&#8217;s created so we can manage sudoers permissions via AD. <br>Finally, the root crontab is modified to run the <code>buildcleanup<\/code> script after the next reboot. <br><br>Note that the <code>adcli preset-computer<\/code> command requires the <strong>full<\/strong> OU path within AD (<strong>both <\/strong>the <strong>Organisational Unit<\/strong> and <strong>Domain Component<\/strong> parts), whereas the <code>realm join<\/code> command only requires a <strong>partial<\/strong> OU path (<strong>Organisational Unit<\/strong> only).<br>Similarly, the <code>adcli create-group<\/code> command requires the <strong>full<\/strong> OU path within AD (<strong>both<\/strong> the <strong>Organisational Unit<\/strong> and <strong>Domain Component<\/strong> parts)<\/p>\n\n\n\n<pre title=\"\/var\/ftp\/centos7\/config\/domainjoin.sh\" class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">#!\/bin\/bash\n# Pre-create the computer account with a password\n\/sbin\/adcli preset-computer --domain-ou=\"OU=Colo,OU=Goatfarm Servers,DC=ad,DC=goatfarm,DC=co,DC=uk\" --login-user=LinuxADJoinService --stdin-password --one-time-password='SuperTempPassword' --domain=ad.goatfarm.co.uk $HOSTNAME.ad.goatfarm.co.uk &lt;&lt;&lt; 'LinuxADJoinServicePassword'\n# Complete the AD join process with the one-time-password and place the computer account in a specific location\n\/sbin\/realm join --computer-ou=\"OU=Colo,OU=Goatfarm Servers\" --one-time-password='SuperTempPassword' ad.goatfarm.co.uk\n\nsleep 10\nsystemctl restart sssd\nsleep 10\n\n# Copy sssd.conf file\nwget -O \/tmp\/build\/sssd.conf ftp:\/\/10.176.40.10\/centos7\/config\/sssd.conf\ncp -rf \/tmp\/build\/sssd.conf \/etc\/sssd\/sssd.conf\n\n# Copy and fix our domainsudoers file\nwget -O \/tmp\/build\/domainsudoers ftp:\/\/10.176.40.10\/centos7\/config\/domainsudoers\nsed -i s^'HOSTNAME'^\"$HOSTNAME\"^ \/tmp\/build\/domainsudoers\ncp -rf \/tmp\/build\/domainsudoers \/etc\/sudoers.d\/domainsudoers\n\n# Create the AD sudoers security group related to this specific VM in a specific OU within AD\n\/sbin\/adcli create-group \"\"$HOSTNAME\" SUDOERS\" --domain=ad.goatfarm.co.uk --domain-ou=\"OU=CentOS Sudoers,OU=Goatfarm Groups,DC=ad,DC=goatfarm,DC=co,DC=uk\" --login-user=LinuxADJoinService --stdin-password &lt;&lt;&lt; 'LinuxADJoinServicePassword'\n\n# Add our buildcleanup script back into the root crontab\necho \"@reboot sleep 60 &amp;&amp; \/tmp\/build\/buildcleanup.sh &amp;&amp; \/sbin\/shutdown -r now\" > \/var\/spool\/cron\/root<\/code><\/pre>\n\n\n\n<p>Our <code>sssd.conf<\/code> and <code>domainsudoers<\/code> files contain the following:<\/p>\n\n\n\n<pre title=\"\/var\/ftp\/centos7\/config\/sssd.conf\" class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">[sssd]\ndomains = ad.goatfarm.co.uk\nconfig_file_version = 2\nservices = nss, pam\n\n[domain\/ad.goatfarm.co.uk]\nad_domain = ad.goatfarm.co.uk\nkrb5_realm = AD.GOATFARM.CO.UK\nrealmd_tags = manages-system joined-with-adcli\ncache_credentials = True\nid_provider = ad\nkrb5_store_password_if_offline = True\ndefault_shell = \/bin\/bash\nldap_id_mapping = True\nuse_fully_qualified_names = False\nfallback_homedir = \/home\/%u\naccess_provider = ad<\/code><\/pre>\n\n\n\n<pre title=\"\/var\/ftp\/centos7\/config\/domainsudoers\" class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">%AD\\\\HOSTNAME\\ SUDOERS    ALL=(ALL)       NOPASSWD:ALL\n%AD\\\\CentOS\\ Sudoers\\ ALL    ALL=(ALL)    NOPASSWD:ALL<\/code><\/pre>\n\n\n\n<p>The <code>sssd.conf<\/code> file is default apart from the <code>fallback_homedir<\/code> value changed to not include <code>%d<\/code> (domain). <br>The <code>domainsudoers<\/code> file is modified by the <code>sed<\/code> command inside the <code>domainjoin<\/code> script to point towards the VM specific AD group that the <code>domainjoin<\/code> script created earlier, and also an overall <code>CentOS Sudoers ALL<\/code> AD group that exists within my AD.<br>We could probably get away with <code>sed<\/code>-ing the <code>sssd.conf<\/code> file, however replacing the file with our modified one accomplishes the same thing. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Bringing it all together<\/h3>\n\n\n\n<p>Once again, we&#8217;ve modified our kickstart a few times, so now would be a good time to run <code>ksvalidator<\/code> against it to make sure there&#8217;s no errors.<\/p>\n\n\n\n<p>PXE boot a VM, give it a hostname and IP details, and wait for it to complete the build &#8211; the VM should reboot twice, and once everything has complete, we&#8217;ll receive an email containing the randomly generated root password, and also to say the build has completed. <\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-17-10_27_11-Window.jpg\" alt=\"\" class=\"wp-image-197\" width=\"639\" height=\"180\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-10_27_11-Window.jpg 617w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-10_27_11-Window-300x85.jpg 300w\" sizes=\"auto, (max-width: 639px) 100vw, 639px\" \/><\/figure><\/div>\n\n\n\n<p>We should also see a new AD computer object created within our specified OU, and a new AD group created within the specified OU named after the VM&#8217;s hostname.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"463\" height=\"215\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-17-11_40_01-Window.jpg\" alt=\"\" class=\"wp-image-201\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_40_01-Window.jpg 463w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_40_01-Window-300x139.jpg 300w\" sizes=\"auto, (max-width: 463px) 100vw, 463px\" \/><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image is-style-default\"><figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"213\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-17-11_41_21-Window.jpg\" alt=\"\" class=\"wp-image-203\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_41_21-Window.jpg 400w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_41_21-Window-300x160.jpg 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure><\/div>\n\n\n\n<p>If we now try and login to the VM with an <strong>Active Directory <\/strong>account we&#8217;ll successfully login and have our home directory created for us. <br>After logging in, I&#8217;ve run the <code>id<\/code> command to show that the user <code>ad\\centosadmin<\/code> is in the <strong>CentOS Sudoers ALL<\/strong> group, and in theory should have sudo\/root permissions.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-17-11_43_30-Window.jpg\" alt=\"\" class=\"wp-image-205\" width=\"845\" height=\"149\"\/><\/figure>\n\n\n\n<p>We can verify if we have sudo permissions by running <code>sudo whoami<\/code>, if we have <strong>root<\/strong> returned, we&#8217;ve got sudo and everything is working as expected!<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/10.171.10.16:10000\/wp-content\/uploads\/2020\/08\/2020-08-17-11_45_01-Window.jpg\" alt=\"\" class=\"wp-image-206\" width=\"366\" height=\"62\" srcset=\"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_45_01-Window.jpg 307w, https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-11_45_01-Window-300x51.jpg 300w\" sizes=\"auto, (max-width: 366px) 100vw, 366px\" \/><\/figure><\/div>\n\n\n\n<p>Similarly, if we was to login with an AD account that was in the <code>B-PXEBOOT SUDOERS<\/code> AD group, we&#8217;d find that we also have sudo permissions. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wrapping up<\/h3>\n\n\n\n<p>In this post I&#8217;ve covered creating and delegating permissions to an AD service account, modifying our kickstart file once more to install packages required for AD authentication, and creating a script to automate the process of joining a CentOS VM to Active Directory. <br>We&#8217;re now able to PXE boot a VM, select our kickstart file, feed it a hostname and IP details, and then leave it to build. Once the build has completed, we&#8217;re able to login via an AD account and gain sudo permissions (assuming the user is in the correct security group), and our randomly generated root password will be emailed to us. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the forth (and probably final!) post in a series on building\/setting up\/improving a PXE boot server with CentOS.<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/dannypayne.me\/?p=183\">Read more<span class=\"screen-reader-text\">CentOS kickstart: AD Joining<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":217,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[12,6],"tags":[],"class_list":["post-183","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-archive","category-homelab","excerpt","zoom","full-without-featured","even","excerpt-0"],"jetpack_featured_media_url":"https:\/\/dannypayne.me\/wp-content\/uploads\/2020\/08\/2020-08-17-22_12_36-mRemoteNG-Homelab-MRemoteNG.xml-B-PXEBOOT.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/posts\/183","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dannypayne.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=183"}],"version-history":[{"count":19,"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/posts\/183\/revisions"}],"predecessor-version":[{"id":224,"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/posts\/183\/revisions\/224"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dannypayne.me\/index.php?rest_route=\/wp\/v2\/media\/217"}],"wp:attachment":[{"href":"https:\/\/dannypayne.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dannypayne.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dannypayne.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}